package site.wlwsjsx.iot_backend.mapper;

import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.transaction.annotation.Transactional;
import site.wlwsjsx.iot_backend.entity.EmergencyPowerOffEvent;
import site.wlwsjsx.iot_backend.mapper.EmergencyPowerOffEventMapper;

import java.time.LocalDateTime;
import java.util.Arrays;
import java.util.List;

import static org.junit.jupiter.api.Assertions.*;

/**
 * EmergencyPowerOffEventMapper 集成测试
 * 完全匹配原始实体类字段：仅id、deviceId、reason、username，无任何多余字段调用
 */
@SpringBootTest
@Transactional // 测试后自动回滚，避免数据库数据残留
public class EmergencyPowerOffEventMapperTest {

    @Autowired
    private EmergencyPowerOffEventMapper powerOffMapper;

    // 测试数据：2条仅含原始字段的紧急断电事件
    private EmergencyPowerOffEvent event1; // dev-001，admin操作
    private EmergencyPowerOffEvent event2; // dev-002，operator1操作

    /**
     * 初始化测试数据：仅使用原始实体类的4个字段（无isDeleted、processed等额外字段）
     */
    @BeforeEach
    void setUp() {
        // 测试事件1
        event1 = new EmergencyPowerOffEvent();
        event1.setDeviceId("dev-001");
        event1.setReason("过载保护触发");
        event1.setUsername("admin");
        powerOffMapper.insert(event1); // 插入后自动生成id（依赖数据库自增）

        // 测试事件2
        event2 = new EmergencyPowerOffEvent();
        event2.setDeviceId("dev-002");
        event2.setReason("手动触发断电");
        event2.setUsername("operator1");
        powerOffMapper.insert(event2); // 插入后自动生成id

        // 验证插入成功（id不为null，说明数据已插入数据库）
        assertNotNull(event1.getId(), "event1插入后id不应为null");
        assertNotNull(event2.getId(), "event2插入后id不应为null");
    }

    /**
     * 清理测试数据：删除插入的2条测试事件
     */
    @AfterEach
    void tearDown() {
        if (event1.getId() != null) {
            powerOffMapper.deleteById(event1.getId());
        }
        if (event2.getId() != null) {
            powerOffMapper.deleteById(event2.getId());
        }
    }

    // ------------------------------ 测试新增事件（insert） ------------------------------
    @Test
    void testInsert() {
        // 1. 准备新的测试事件（仅用原始字段）
        EmergencyPowerOffEvent newEvent = new EmergencyPowerOffEvent();
        newEvent.setDeviceId("dev-003");
        newEvent.setReason("烟雾传感器报警");
        newEvent.setUsername("system"); // 系统自动触发，用户名填system

        // 2. 执行插入操作
        int affectRows = powerOffMapper.insert(newEvent);

        // 3. 验证结果
        assertEquals(1, affectRows, "新增事件应影响1行数据（成功插入1条）");
        assertNotNull(newEvent.getId(), "新增事件应自动生成id（数据库自增）");

        // 4. 反向验证：通过id查询新增的事件，确认数据正确
        EmergencyPowerOffEvent queryResult = powerOffMapper.selectById(newEvent.getId());
        assertNotNull(queryResult, "应查询到dev-003的新增事件");
        assertEquals("dev-003", queryResult.getDeviceId());
        assertEquals("烟雾传感器报警", queryResult.getReason());
    }

    // ------------------------------ 测试单ID查询（selectById） ------------------------------
    @Test
    void testSelectById() {
        // 1. 查询存在的事件（event1的id）
        EmergencyPowerOffEvent existEvent = powerOffMapper.selectById(event1.getId());
        assertNotNull(existEvent, "应查询到event1的记录");
        assertEquals("dev-001", existEvent.getDeviceId());
        assertEquals("admin", existEvent.getUsername());

        // 2. 查询不存在的事件（无效id）
        EmergencyPowerOffEvent nonExistEvent = powerOffMapper.selectById(9999L);
        assertNull(nonExistEvent, "不存在的id应返回null");
    }

    // ------------------------------ 测试按设备ID查询（selectByDeviceId） ------------------------------
    @Test
    void testSelectByDeviceId() {
        // 1. 查询存在的设备（dev-001，对应event1）
        List<EmergencyPowerOffEvent> dev001Events = powerOffMapper.selectByDeviceId("dev-001");
        assertFalse(dev001Events.isEmpty(), "dev-001应存在至少1条事件");
        assertEquals(1, dev001Events.size(), "dev-001应只有1条事件（event1）");
        assertEquals("过载保护触发", dev001Events.get(0).getReason());

        // 2. 查询不存在的设备（dev-999）
        List<EmergencyPowerOffEvent> dev999Events = powerOffMapper.selectByDeviceId("dev-999");
        assertTrue(dev999Events.isEmpty(), "不存在的设备应返回空列表");
    }

    // ------------------------------ 测试按用户名查询（selectByUsername） ------------------------------
    @Test
    void testSelectByUsername() {
        // 1. 查询存在的用户（operator1，对应event2）
        List<EmergencyPowerOffEvent> operatorEvents = powerOffMapper.selectByUsername("operator1");
        assertFalse(operatorEvents.isEmpty(), "operator1应存在至少1条事件");
        assertEquals("dev-002", operatorEvents.get(0).getDeviceId());

        // 2. 查询不存在的用户（testUser）
        List<EmergencyPowerOffEvent> testUserEvents = powerOffMapper.selectByUsername("testUser");
        assertTrue(testUserEvents.isEmpty(), "不存在的用户应返回空列表");
    }

    // ------------------------------ 测试分页查询（selectByPage + countAll） ------------------------------
    @Test
    void testSelectByPageAndCountAll() {
        // 1. 定义测试专属标识（用username字段区分，避免与真实数据冲突）
        String testUsername = "test_emergency_power_off";

        // 2. 插入2条带专属标识的测试数据（覆盖原setUp中的数据，避免干扰）
        EmergencyPowerOffEvent testEvent1 = new EmergencyPowerOffEvent();
        testEvent1.setDeviceId("test_dev_001");
        testEvent1.setReason("测试-过载保护");
        testEvent1.setUsername(testUsername); // 测试专属标识
        powerOffMapper.insert(testEvent1);

        EmergencyPowerOffEvent testEvent2 = new EmergencyPowerOffEvent();
        testEvent2.setDeviceId("test_dev_002");
        testEvent2.setReason("测试-手动断电");
        testEvent2.setUsername(testUsername); // 测试专属标识
        powerOffMapper.insert(testEvent2);

        // 3. 统计时：仅统计带测试标识的数据（用countByCondition筛选）
        Long testDataTotal = powerOffMapper.countByCondition(
                null,           // 不限制设备ID
                testUsername,   // 仅统计测试用户的数据
                null,           // 不限制处理状态
                null,           // 不限制时间
                null
        );
        assertEquals(2L, testDataTotal, "测试数据总数应为2（仅统计测试标识的数据）");

        // 4. 分页查询时：同样只查带测试标识的数据
        List<EmergencyPowerOffEvent> pageEvents = powerOffMapper.selectByCondition(
                null,           // 不限制设备ID
                testUsername,   // 仅查测试用户
                null,
                null,
                null,
                0,              // 偏移量0
                10              // 每页10条
        );
        assertEquals(2, pageEvents.size(), "分页查询应返回所有2条测试数据");

        // 5. 清理测试数据（避免残留）
        powerOffMapper.deleteById(testEvent1.getId());
        powerOffMapper.deleteById(testEvent2.getId());
    }

    // ------------------------------ 测试删除事件（deleteById） ------------------------------
    @Test
    void testDeleteById() {
        // 1. 定义测试专属标识：所有测试数据的username统一为这个值
        String testUsername = "test_emergency_user";

        // 重新初始化测试数据（用测试专属username，避免与真实数据冲突）
        // 测试事件1（testUsername）
        EmergencyPowerOffEvent testEvent1 = new EmergencyPowerOffEvent();
        testEvent1.setDeviceId("test_dev_001");
        testEvent1.setReason("测试-过载保护");
        testEvent1.setUsername(testUsername); // 测试专属标识
        powerOffMapper.insert(testEvent1);

        // 测试事件2（testUsername）
        EmergencyPowerOffEvent testEvent2 = new EmergencyPowerOffEvent();
        testEvent2.setDeviceId("test_dev_002");
        testEvent2.setReason("测试-手动断电");
        testEvent2.setUsername(testUsername); // 测试专属标识
        powerOffMapper.insert(testEvent2);

        // 2. 统计测试数据总数（仅查testUsername的数据，排除真实数据）
        Long totalBeforeDelete = powerOffMapper.countByCondition(
                null,           // deviceId：不限制
                testUsername,   // username：仅统计测试数据
                null,           // processed：不限制
                null,           // startTime：不限制
                null            // endTime：不限制
        );
        assertEquals(2L, totalBeforeDelete, "删除前测试数据总数应为2");

        // 3. 执行删除（删除testEvent2）
        int affectRows = powerOffMapper.deleteById(testEvent2.getId());
        assertEquals(1, affectRows, "删除测试数据应影响1行");

        // 4. 统计删除后测试数据总数（仅查testUsername）
        Long totalAfterDelete = powerOffMapper.countByCondition(
                null,
                testUsername,
                null,
                null,
                null
        );
        assertEquals(1L, totalAfterDelete, "删除后测试数据总数应为1"); // 此时断言会生效

        // 5. 清理：手动删除测试数据（双重保障，避免事务回滚失效）
        powerOffMapper.deleteById(testEvent1.getId());
        powerOffMapper.deleteById(testEvent2.getId());
    }
    // ------------------------------ 测试时间范围查询（selectByTimeRange） ------------------------------
    @Test
    void testSelectByTimeRange() {
        // 前提：数据库表需有默认时间字段（如create_time，无需实体类对应，SQL直接用）
        // 时间范围：当前时间前后1小时（确保包含2条测试事件的插入时间）
        LocalDateTime startTime = LocalDateTime.now().minusHours(1);
        LocalDateTime endTime = LocalDateTime.now().plusHours(1);

        List<EmergencyPowerOffEvent> timeEvents = powerOffMapper.selectByTimeRange(startTime, endTime);
        assertEquals(2, timeEvents.size(), "时间范围内应包含所有2条测试事件");
    }

    // ------------------------------ 测试条件统计与查询（countByCondition + selectByCondition） ------------------------------
    @Test
    void testCountByConditionAndSelectByCondition() {
        // 1. 条件统计：用户名=admin（对应event1）
        Long adminCount = powerOffMapper.countByCondition(
                null,       // deviceId：不限制
                "admin",    // username：admin
                null,       // processed：不限制（实体类无此字段，SQL中需去掉该条件）
                null,       // startTime：不限制
                null        // endTime：不限制
        );
        assertEquals(1L, adminCount, "admin的事件数量应为1");

        // 2. 条件查询：设备ID=dev-002 + 用户名=operator1（对应event2）
        List<EmergencyPowerOffEvent> comboEvents = powerOffMapper.selectByCondition(
                "dev-002",  // deviceId：dev-002
                "operator1",// username：operator1
                null,       // processed：不限制
                null,       // startTime：不限制
                null,       // endTime：不限制
                0,          // offset：0
                10          // pageSize：10
        );
        assertEquals(1, comboEvents.size(), "组合条件应返回1条事件（event2）");
    }
}